using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;
using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;

namespace CKY.AgentPlatform.Performance
{
    /// <summary>
    /// 响应缓存中间件
    /// 用于缓存GET请求的响应结果，提高API性能
    /// </summary>
    public class CachingMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly IDistributedCache _cache;
        private readonly ILogger<CachingMiddleware> _logger;
        private readonly CachingOptions _options;

        public CachingMiddleware(
            RequestDelegate next,
            IDistributedCache cache,
            ILogger<CachingMiddleware> logger,
            CachingOptions options)
        {
            _next = next;
            _cache = cache;
            _logger = logger;
            _options = options;
        }

        public async Task InvokeAsync(HttpContext context)
        {
            // 只缓存GET请求
            if (context.Request.Method != HttpMethod.Get.Method)
            {
                await _next(context);
                return;
            }

            // 检查是否应该缓存此路径
            var path = context.Request.Path.Value;
            if (!_options.ShouldCache(path))
            {
                await _next(context);
                return;
            }

            // 生成缓存键
            var cacheKey = GenerateCacheKey(context.Request);
            var originalBody = context.Response.Body;

            try
            {
                // 尝试从缓存获取
                var cachedResponse = await _cache.GetAsync(cacheKey);
                if (cachedResponse != null)
                {
                    _logger.LogDebug("缓存命中: {CacheKey}", cacheKey);

                    // 设置缓存头信息
                    context.Response.Headers.Add("X-Cache", "HIT");
                    context.Response.Headers.Add("X-Cache-Key", cacheKey);

                    // 返回缓存响应
                    context.Response.ContentType = "application/json";
                    await context.Response.Body.WriteAsync(cachedResponse, 0, cachedResponse.Length);
                    return;
                }

                _logger.LogDebug("缓存未命中: {CacheKey}", cacheKey);

                // 执行请求并缓存响应
                var originalBodyForProcessing = context.Response.Body;
                using var newBody = new MemoryStream();
                context.Response.Body = newBody;

                await _next(context);

                if (context.Response.StatusCode == 200)
                {
                    // 重置响应体位置
                    newBody.Seek(0, SeekOrigin.Begin);
                    var responseBody = new StreamReader(newBody).ReadToEnd();
                    var responseBytes = Encoding.UTF8.GetBytes(responseBody);

                    // 缓存响应
                    var cacheOptions = new DistributedCacheEntryOptions
                    {
                        AbsoluteExpirationRelativeToNow = _options.GetExpiration(path)
                    };

                    await _cache.SetAsync(cacheKey, responseBytes, cacheOptions);

                    _logger.LogDebug("响应已缓存: {CacheKey}, 过期时间: {Expiration}",
                        cacheKey, cacheOptions.AbsoluteExpirationRelativeToNow);

                    // 恢复响应体并写入原始流
                    newBody.Seek(0, SeekOrigin.Begin);
                    context.Response.Headers.Add("X-Cache", "MISS");
                    await newBody.CopyToAsync(originalBody);
                }
                else
                {
                    // 对于非200响应，不缓存但需要恢复响应体
                    newBody.Seek(0, SeekOrigin.Begin);
                    await newBody.CopyToAsync(originalBody);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "缓存中间件处理失败: {CacheKey}", cacheKey);
                // 缓存失败时继续执行原始请求
                await _next(context);
            }
            finally
            {
                // 确保响应体被恢复
                if (context.Response.Body is MemoryStream)
                {
                    context.Response.Body = originalBody;
                }
            }
        }

        private string GenerateCacheKey(HttpRequest request)
        {
            var keyBuilder = new StringBuilder();
            keyBuilder.Append(request.Path);

            // 添加查询参数
            if (request.QueryString.HasValue)
            {
                keyBuilder.Append(request.QueryString.Value);
            }

            // 添加用户信息（如果需要用户级别的缓存）
            var userId = request.HttpContext.User?.Identity?.Name;
            if (!string.IsNullOrEmpty(userId))
            {
                keyBuilder.Append($"|user:{userId}");
            }

            return $"CACHE:{keyBuilder.ToString().GetHashCode()}";
        }
    }

    /// <summary>
    /// 缓存配置选项
    /// </summary>
    public class CachingOptions
    {
        private readonly List<CacheRule> _cacheRules = new();

        /// <summary>
        /// 默认缓存时间
        /// </summary>
        public TimeSpan DefaultExpiration { get; set; } = TimeSpan.FromMinutes(5);

        /// <summary>
        /// 添加缓存规则
        /// </summary>
        public void AddRule(string pathPattern, TimeSpan expiration)
        {
            _cacheRules.Add(new CacheRule(pathPattern, expiration));
        }

        /// <summary>
        /// 检查是否应该缓存指定路径
        /// </summary>
        public bool ShouldCache(string path)
        {
            return _cacheRules.Any(rule => rule.IsMatch(path));
        }

        /// <summary>
        /// 获取指定路径的缓存过期时间
        /// </summary>
        public TimeSpan GetExpiration(string path)
        {
            var rule = _cacheRules.FirstOrDefault(r => r.IsMatch(path));
            return rule?.Expiration ?? DefaultExpiration;
        }
    }

    /// <summary>
    /// 缓存规则
    /// </summary>
    public class CacheRule
    {
        private readonly string _pathPattern;
        private readonly TimeSpan _expiration;

        public CacheRule(string pathPattern, TimeSpan expiration)
        {
            _pathPattern = pathPattern;
            _expiration = expiration;
        }

        public TimeSpan Expiration => _expiration;

        public bool IsMatch(string path)
        {
            if (_pathPattern == "*") return true;
            if (_pathPattern.EndsWith("*"))
            {
                var prefix = _pathPattern.Substring(0, _pathPattern.Length - 1);
                return path.StartsWith(prefix);
            }
            return path.Equals(_pathPattern, StringComparison.OrdinalIgnoreCase);
        }
    }
}